/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 *         
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Unless required by applicable law or
 * agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package org.rhok.pdx.ncn.background;

import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.rhok.pdx.ncn.communicator.WebClient;
import org.rhok.pdx.ncn.datatypes.LocationData;
import org.rhok.pdx.ncn.storage.NCNDatabaseManager;

import android.os.AsyncTask;

/**
 * Background process to communicate with the server at regular intervals to both submit data as
 * well as receive new updates from the server.
 * @author pfritzsche
 *
 */
public class ServerSyncerTask extends AsyncTask<Void, Void, Void> {
	public interface ServerSyncerContext {
		public ConcurrentLinkedQueue<LocationData> locs();
		public NCNDatabaseManager db();
	}
	
	private static final int
		MIN_DELAY = 30000,
		MAX_DELAY = 600000;
	private final ServerSyncerContext context;
	private final WebClient client;
	
	private int delay = 1000;
	
	/**
	 * Public constructor
	 * @param context Shared application context, to be used for data sharing between classes
	 */
	public ServerSyncerTask(ServerSyncerContext context) {
		this.context = context;
		client = new WebClient();
	}
	
	/**
	 * Thread runner. Periodically syncs with the server, submitting new data points and downloading
	 * updates from the server. Uses additive increase, multiplicative decrease for the time delay
	 * between attempted syncs to try and save battery.
	 */
	@Override
	protected Void doInBackground(Void... params) {
		while (!isCancelled()) {
			if (context.locs().size() > 5) {
				try {
					boolean result = client.submitData(context.locs());
					updateDelay(result);
					
					if (result) {
						List<LocationData> data = client.getServerData(
								context.db().getMostRecentLocationData());
						context.db().addRows(data);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	
	/**
	 * Updates the current amount of time being used for the delay
	 * @param result true if the previous sync attempt succeeded
	 */
	private void updateDelay(boolean result) {
		if (!result) {
			if (delay <= MAX_DELAY) {
				delay = (delay > MAX_DELAY / 2) ? MAX_DELAY : delay * 2;
			}
		} else {
			if (delay >= MIN_DELAY) {
				delay = (delay < MIN_DELAY + 10000) ? MIN_DELAY : delay - 10000;
			}
		}
	}
}
